package com.weedsdream.app.compose

import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.calculateCentroid
import androidx.compose.foundation.gestures.calculatePan
import androidx.compose.foundation.gestures.calculateZoom
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.toOffset
import com.weedsdream.app.colorplate.ColorSetData
import com.weedsdream.app.colorplate.ColorSetType
import com.weedsdream.app.colorplate.colorSet
import com.weedsdream.app.colorplate.curRGB
import com.weedsdream.app.colorplate.emphasizeCurColor
import com.weedsdream.app.colorplate.penSize
import com.weedsdream.app.compose.ToolType.橡皮擦
import com.weedsdream.app.compose.ToolType.笔
import com.weedsdream.app.compose.ToolType.选择
import com.weedsdream.app.function.fullView
import com.weedsdream.app.struct.Path
import com.weedsdream.app.struct.drafts

/**单指触摸-绘图或擦除*/
private var isSingleTouch by mutableStateOf(false)

/**正在操作的路径*/
private var curPath by mutableStateOf<Path?>(null)

/**橡皮擦尺寸大小*/
val eraserSize = Offset(100f, 100f)

/**绘图手势检测层*/
@Composable
fun BoardSpaceGesture() {
    Spacer(Modifier
        .fillMaxSize()
        .alpha(0.3f)
        .pointerInput(Unit) {//绘图   多指：缩放、平移
            awaitEachGesture { onSlide() }
        }
        .pointerInput(Unit) {//双击全览
            detectTapGestures(onDoubleTap = { fullView() })
        })
}

/**手指在屏幕上滑动时*/
suspend fun AwaitPointerEventScope.onSlide() {
    while (true) {
        val pointers = awaitPointerEvent()
        val events = pointers.changes
        if (events.size == 1) {
            if (events[0].pressed) {//手势按下/滑动
                val event = events[0]
                val srcPos = event.position//转换前屏幕坐标
                val pos =//屏幕坐标转换到缩放影响后
                    (srcPos - scCenter!! - boxOffset.toOffset()) / boxScale + scCenter!!
                if (event.previousPressed && isSingleTouch) {//滑动，绘图或擦除
                    if (curTool == 笔) {
                        curPath!!.points.add(pos)
                    } else if (curTool == 橡皮擦) {
                        esrIOft = Pair(true, srcPos - eraserSize)//橡皮擦手指跟随
                        if (emphasizeCurColor) drafts[curRGB]?.let { removePoint(pos, it) }
                        else drafts.values.forEach { removePoint(pos, it) }
                    } else if (curTool == 选择) {
                        if (dragPos && chosenPaths.isNotEmpty()) {//选择路径跟随手指移动
                            val dis = pos - chosePath2pressPos//计算与上次移动的位置变化差
                            for (paths in chosenPaths.values) for (path in paths) {
                                path.movePath(dis.x, dis.y)
                            }
                            chosePath2pressPos = pos
                        } else choosePath(pos)
                    }
                } else {//按下，初始化（多指操作退化的单指事件不经过此部分代码）
                    if (curTool == 笔) {
                        curPath = Path(penSize)
                        val colorSetData = ColorSetData(curRGB)
                        val paths = drafts[curRGB] ?: run {
                            val newPath = mutableStateListOf<Path>()
                            drafts[curRGB] = newPath
                            colorSet[ColorSetType.使用时间]!!.add(colorSetData)
                            newPath
                        }
                        for (c in colorSet[ColorSetType.使用时间]!!) if (c == colorSetData) {
                            c.time = System.currentTimeMillis()
                            break
                        }
                        paths.add(curPath!!)
                    } else if (curTool == 选择) {
                        if (dragPos && chosenPaths.isNotEmpty()) chosePath2pressPos = pos
                    }
                    isSingleTouch = true
                }
            } else {//手势抬起,隐藏橡皮擦或清理空数据
                if (curTool == 笔) {
                    curPath = null
                } else if (curTool == 橡皮擦) {
                    if (esrIOft.first) esrIOft = Pair(false, Offset.Zero)
                }
                isSingleTouch = false
            }
        } else {
            //缩放-基于屏幕中心向四周变化
            val zoom = pointers.calculateZoom()//缩放变化值
            boxScale *= zoom
            //恢复缩放导致的偏移,使缩放以手势中心为基准变化
            val dis = pointers.calculateCentroid() - scCenter!!//手势中心与屏幕中心距离
            val boxOft = dis * (1 - zoom) + boxOffset.toOffset() * zoom
            //平移
            val oft = pointers.calculatePan() + boxOft
            boxOffset = IntOffset(oft.x.toInt(), oft.y.toInt())

            isSingleTouch = false
        }
        events.forEach { it.consume() }
    }
}

/**移除指定颜色的点集
 * @param erePos 橡皮擦中心位置
 * @param paths 受擦除影响的线条集*/
private fun removePoint(erePos: Offset, paths: SnapshotStateList<Path>) {
    val eOst1 = eraserSize / boxScale//受缩放影响后的实际范围
    val ltOst = erePos - eOst1//左上角生效位置
    val rbOst = erePos + eOst1//右下角生效位置

    val needRemove = mutableMapOf<Path, ArrayList<Offset>>()//<要被分割的路径,path被移除的点>
    for (path in paths) {
        val nrp = arrayListOf<Offset>()
        for (point in path.points) if (point.x >= ltOst.x && point.y >= ltOst.y && point.x <= rbOst.x && point.y <= rbOst.y) {
            nrp.add(point)
        }
        if (nrp.isNotEmpty()) needRemove[path] = nrp
    }
    for ((path, rmPoints) in needRemove) {
        if (rmPoints.size >= path.points.size - 1) {//删除点导致原路径只有最多1个点，无法显示，直接删除路径
            paths.remove(path)
            continue
        }
        val points = path.points//原点集
        var curPs = Path(path.penSize)//当前记录的分割线
        val p2ps = arrayListOf<Path>()//分割后的线集合
        var ip = 0//原点集遍历位置
        var ir = 0//移除集遍历位置
        while (ip < points.size) {
            if (points[ip] == rmPoints[ir]) {//遇到被移除的点
                if (curPs.points.isNotEmpty()) {
                    if (curPs.points.size == 1) curPs.points.clear()//路径过短
                    else {//保存上一条路径并新建路径
                        p2ps.add(curPs)
                        curPs = Path(path.penSize)
                    }
                }
                ir++
                if (ir == rmPoints.size) {//已经是最后一个要移除的点，后面的点全部属于一条新的路径
                    ip++
                    while (ip < points.size) {
                        curPs.points.add(points[ip])
                        ip++
                    }
                    if (curPs.points.size > 1) p2ps.add(curPs)//保存最后的路径
                    break
                }
            } else curPs.points.add(points[ip])
            ip++
        }
        paths.remove(path)//移除原路径
        paths.addAll(p2ps)//添加被分割后的路径
    }
}

/**选中指定的一条路径，当前颜色优先级最高
 * @param pos 手指中心位置*/
private fun choosePath(pos: Offset) {
    val eOst1 = eraserSize / boxScale//受缩放影响后的实际范围
    val ltOst = pos - eOst1//左上角生效位置
    val rbOst = pos + eOst1//右下角生效位置

    if (!mulChoice) chosenPaths.clear()//单选模式只选一条，清除已选条目
    if (emphasizeCurColor) drafts[curRGB]?.let {
        choseOne(curRGB, it, ltOst, rbOst)
    } else for ((color, paths) in drafts) {
        if (!choseOne(color, paths, ltOst, rbOst)) return
    }
}

/**添加路径
 * @return false 不再检查剩余的路径*/
private fun choseOne(
    color: Color, paths: SnapshotStateList<Path>, ltOst: Offset, rbOst: Offset
): Boolean {
    for (path in paths) {
        if (chosenPaths[color] == null) chosenPaths[color] = mutableStateListOf()
        else if (chosenPaths[color]!!.contains(path)) continue//跳过已选
        if (check(path, ltOst, rbOst)) {
            chosenPaths[color]!!.add(path)
            if (!mulChoice) return false //单选状态只要添加一条就结束
        }
    }
    return true
}

/**检查指定路径是否被触摸*/
private fun check(path: Path, ltOst: Offset, rbOst: Offset): Boolean {
    for (point in path.points) if (point.x >= ltOst.x && point.y >= ltOst.y && point.x <= rbOst.x && point.y <= rbOst.y) return true
    return false
}